package com.cabal.auth.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cabal.auth.dao.CabalAuthMapper;
import com.cabal.auth.dao.CabalCharacterGmBuffMapper;
import com.cabal.auth.dao.CabalCharacterNationMapper;
import com.cabal.auth.dao.CabalGatewayGameConfigMapper;
import com.cabal.auth.dto.*;
import com.cabal.auth.entity.*;
import com.cabal.auth.service.ICabalAuthService;
import com.cabal.auth.vo.CabalAuthVo;
import com.cabal.common.constants.SystemConstants;
import com.cabal.common.util.*;
import com.cabal.config.DynamicDataSourceContextHolder;
import com.cabal.contribute.dao.CabalContributePointAccountMapper;
import com.cabal.contribute.entity.CabalContributePointAccount;
import com.cabal.mall.dao.CabalCharacterInfoMapper;
import com.cabal.mall.dao.CabalMallCoinAccountMapper;
import com.cabal.mall.dao.CabalMallCoinLogMapper;
import com.cabal.mall.entity.CabalCharacterInfo;
import com.cabal.mall.entity.CabalMallCoinAccount;
import com.cabal.mall.entity.CabalMallCoinLog;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.Charset;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.*;

@Slf4j
@Service
public class CabalAuthServiceImpl implements ICabalAuthService {

    private static final String CABAL_ONLINE_CODE = "CABAL_ONLINE_CODE_";

    private static final String CABAL_STATIC_ONLINE_CODE = "CABAL_STATIC_ONLINE_CODE_";

    @Value("${md5.files}")
    private String md5Files;

    @Value("${check.account.time:10}")
    private Integer checkAccountTime;

    @Value("${check.account.online_count_limit:2}")
    private Integer onlineAccountLimit;

    @Value("${onlineCash.vip_basic_multiplier:0.2}")
    private String onlineCashVipBasicMultiplier;

    @Value("${updatePassword.on-off:true}")
    private Boolean updatePasswordOn;

    /*@Value("${cashMultiple:1}")
    private Integer cashMultiple;*/

    @Resource
    private CabalGatewayGameConfigMapper cabalGatewayGameConfigMapper;

    @Resource
    private CabalAuthMapper cabalAuthMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private CabalMallCoinAccountMapper cabalMallCoinAccountMapper;

    @Resource
    private CabalMallCoinLogMapper cabalMallCoinLogMapper;

    @Resource
    private CabalContributePointAccountMapper cabalContributePointAccountMapper;

    @Resource
    private CabalCharacterNationMapper cabalCharacterNationMapper;

    @Resource
    private CabalCharacterGmBuffMapper cabalCharacterGmBuffMapper;

    @Resource
    private CabalCharacterInfoMapper cabalCharacterInfoMapper;

    private static  Map<String, String> resultMap = new HashMap<>();

    @Override
    public List<CabalAuthVo> searchOnlineUserList() {
        List<CabalAuthVo> cabalAuthVoList = cabalAuthMapper.searchOnlineUserList();
        cabalAuthVoList.stream().forEach(p -> p.setLastLoginIp(p.getLastLoginIp().replace(" ", "")));
        return cabalAuthVoList;
    }

    @Override
    public void downloadUpdateFile(CabalAuthDTO cabalAuthDTO, HttpServletResponse response) {
        InputStream inStream = null;
        File file = null;
        try {
            file = new File(System.getProperty("user.dir").concat("/template/update.zip"));
            response.setContentType("application/octet-stream; charset=UTF-8");
            String name = "update.zip";
            response.setHeader("Content-Disposition", "attachment; filename=\"" + name + "\"");

            // 循环取出流中的数据
            byte[] b = new byte[1024];
            int len;
            inStream = new FileInputStream(file);
            response.reset();
            ServletOutputStream outputStream = response.getOutputStream();
            while ((len = inStream.read(b)) > 0) {
                outputStream.write(b, 0, len);
            }
            outputStream.flush();
            outputStream.close();
        } catch (Exception e) {
            log.error("文件下载异常");
        } finally {
            if(inStream != null) {
                try {
                    inStream.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            /*if(file != null) {
                file.delete();
            }*/
        }
    }

    @Override
    public String getLatestVersion() {
        // 查询数据库表读取版本号
        String version;
        try {
            DynamicDataSourceContextHolder.setContextKey(SystemConstants.DS_KEY_CABALMALL);
            CabalGatewayGameConfig cabalGatewayGameConfig = cabalGatewayGameConfigMapper.selectById(1);
            version = cabalGatewayGameConfig.getClientVersion();
            log.info("读取客户端版本号>>>>{}", version);
        } finally {
            DynamicDataSourceContextHolder.removeContextKey();
        }
        return version;
    }

    @Override
    public String receiveClientInfo(CabalClientInfoDTO cabalClientInfoDTO) {
        // 根据IP和机器码查询记录是否存在，如果存在则执行更新操作
        Long id = cabalAuthMapper.getLoginAccountExtendId(cabalClientInfoDTO.getClientIp());
        int count;
        if(id == null) {
            LoginAccountExtend loginAccountExtend = new LoginAccountExtend();
            loginAccountExtend.setId(SnowflakeIdUtil.getInstance().nextId());
            loginAccountExtend.setLoginIp(cabalClientInfoDTO.getClientIp());
            loginAccountExtend.setMachineCode(cabalClientInfoDTO.getMachineCode());
            LocalDateTime localDateTimeOfNow = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
            loginAccountExtend.setCreateTime(localDateTimeOfNow);
            loginAccountExtend.setUpdateTime(localDateTimeOfNow);
            loginAccountExtend.setAccountState(1);
            count = cabalAuthMapper.insertLoginAccountExtend(loginAccountExtend);
        } else {
            LocalDateTime localDateTimeOfNow = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
            LoginAccountExtend loginAccountExtend = new LoginAccountExtend();
            loginAccountExtend.setId(id);
            loginAccountExtend.setUpdateTime(localDateTimeOfNow);
            count = cabalAuthMapper.updateLoginAccountExtend(loginAccountExtend);
        }

        return count > 0 ? "SUCCESS" : "";
    }

    @Override
    public String updateAccountPassword(UpdateAccountPasswordDTO updateAccountPasswordDTO, String ipAddress) {
        DynamicDataSourceContextHolder.removeContextKey();
        String account = updateAccountPasswordDTO.getAccount();
        String clientId = updateAccountPasswordDTO.getClientId();
        stringRedisTemplate.opsForValue().set(clientId, account);
        stringRedisTemplate.opsForValue().set(account, clientId);

        log.info("{}-{}正在登录帐号，执行同步更新密码操作", account, clientId);
        if(!updatePasswordOn) {
            log.info("{}禁止登录", updateAccountPasswordDTO.getAccount());
            return "暂未开放登录";
        }

        // 根据IP查询同IP在线账号个数，超过限定个数则直接返回
        DynamicDataSourceContextHolder.removeContextKey();
        int countAccountOnlineByIp = cabalAuthMapper.countAccountOnlineByIp(ipAddress);
        CabalAuthVo dbCabalAuthVo = cabalAuthMapper.getCabalAuthVoByAccount(account);
        if(countAccountOnlineByIp >= onlineAccountLimit && StringUtils.isNotBlank(dbCabalAuthVo.getPhone())) {
            log.info("同IP超过同时在线账号个数，直接返回>>>>" + ipAddress);
            return "同IP在线账号超限";
        }

        if(!StringUtils.isEmpty(dbCabalAuthVo.getPhone()) && stringRedisTemplate.hasKey(SystemConstants.CABAL_BLACK_PHONE_KEY.concat(dbCabalAuthVo.getPhone()))) {
            // 存在黑户手机号，封禁账号直接return
            log.info("根据手机号黑户封禁>>>>" + dbCabalAuthVo.getPhone());
            cabalAuthMapper.updateAuthTypeByPhone(dbCabalAuthVo.getPhone());
            return "该账号关联手机号已被列入黑名单";
        }

        // 禁用IP限制处理
        // 根据IP查询是否有禁用账号，如果有则封禁该账号
        Boolean boolAuthKey = true;
        List<CabalAuthVo> dbIpCabalAuthVoList = cabalAuthMapper.getCabalAuthVoByIp(ipAddress);
        if(!CollectionUtils.isEmpty(dbIpCabalAuthVoList)) {
            for(CabalAuthVo dbIpCabalAuthVo : dbIpCabalAuthVoList) {
                if(dbIpCabalAuthVo != null) {
                    log.info("根据IP地址黑户封禁>>>>" + dbIpCabalAuthVo.getPhone());
                    cabalAuthMapper.updateAuthTypeByPhone(dbIpCabalAuthVo.getPhone());
                    boolAuthKey = false;
                }
            }
        }

        if(!boolAuthKey) {
            return "该账号关联IP已被列入黑名单";
        }

        cabalAuthMapper.updatePasswordByAccount(updateAccountPasswordDTO.getAccount(), ipAddress);
        if(dbCabalAuthVo.getLoginCounter() == 0) {
            cabalAuthMapper.updatePasswordByAccountAndAuthType(updateAccountPasswordDTO.getAccount(), ipAddress);
        }

        //log.info("执行更新扩展表账号信息操作，通过IP{}进行更新", ipAddress);
        LocalDateTime localDateTimeOfNow = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
        LoginAccountExtend loginAccountExtend = new LoginAccountExtend();
        loginAccountExtend.setUpdateTime(localDateTimeOfNow);
        /*CabalClientInfoDTO cabalClientInfoDTO = new CabalClientInfoDTO();
        cabalClientInfoDTO.setClientIp(ipAddress);
        receiveClientInfo(cabalClientInfoDTO);*/

        // 查询当前账号是否存在记录，存在则更新IP
        int countAccount = cabalAuthMapper.countAccountByAccount(updateAccountPasswordDTO.getAccount());
        if(countAccount > 0) {
            loginAccountExtend.setAccountName(updateAccountPasswordDTO.getAccount());
            loginAccountExtend.setLoginIp(ipAddress);
            loginAccountExtend.setCreateTime(localDateTimeOfNow);
            loginAccountExtend.setUpdateTime(localDateTimeOfNow);
            cabalAuthMapper.updateInfoByAccount(loginAccountExtend);
        } else {
            cabalAuthMapper.updateAccountNameByIpAddress(updateAccountPasswordDTO.getAccount(), ipAddress);
        }

        // 更新角色商店状态
        int iUserNum = dbCabalAuthVo.getUserNum();
        int startCharacterId = iUserNum * 8;
        for(int i = 0; i < 6; i++) {
            updateShopState(startCharacterId);
            startCharacterId ++;
        }

        return "SUCCESS";
    }

    private void updateShopState(int characterIdx) {
        QueryWrapper<CabalCharacterInfo> queryWrapper = new QueryWrapper<CabalCharacterInfo>();
        queryWrapper.lambda().eq(CabalCharacterInfo::getCharacterId, characterIdx);
        DynamicDataSourceContextHolder.setContextKey(SystemConstants.DS_KEY_CABALMALL);
        int count = cabalCharacterInfoMapper.selectCount(queryWrapper);
        if(count > 0) {
            CabalCharacterInfo updateCabalCharacterInfo = new CabalCharacterInfo();
            updateCabalCharacterInfo.setCharacterId(characterIdx);
            updateCabalCharacterInfo.setShopState(0);
            cabalCharacterInfoMapper.updateById(updateCabalCharacterInfo);
        }
        DynamicDataSourceContextHolder.removeContextKey();
    }

    @Override
    public void updateAbnormalAccount() {
        // 查询账号在线且账号扩张信息表更新时间在1分钟之前的账号标记为异常
        List<String> accountNameList = cabalAuthMapper.searchAbnormalAccountList(checkAccountTime);
        if(CollectionUtils.isEmpty(accountNameList)) {
            log.info("无异常账号");
            return;
        }

        for(String accountName : accountNameList) {
            log.info("异常账号{}>>>>执行强制下线操作", accountName);
            // 对异常账号进行操作，踢下线，并标记异常账号状态
            cabalAuthMapper.updateAccountAuthType(accountName);
        }
    }

    @Override
    public Map<String, String> getFileMd5Value() {
        if(resultMap.size() > 0) {
            return resultMap;
        }

        setFileMd5ResultMap();
        return resultMap;
    }

    private void setFileMd5ResultMap() {
        String[] files = md5Files.split(",");
        for(String fileName : files) {
            resultMap.put(fileName, getMd5ValueByFileName(fileName));
        }
        String tmpFileName = "CabalMain_1437.exe";
        resultMap.put(tmpFileName, getMd5ValueByFileName(tmpFileName));

        String cabalFileName = "cabal.enc";
        resultMap.put(cabalFileName, getMd5ValueByFileName(cabalFileName));
    }

    @Override
    public String updateLoginState(CabalClientInfoDTO cabalClientInfoDTO) {
        /*String account = cabalAuthMapper.getLoginAccountExtendAccountName(cabalClientInfoDTO.getClientIp());
        if(StringUtils.isEmpty(account)) {
            return "SUCCESS";
        }
        cabalAuthMapper.updateLoginState(account);

        // 根据帐号清除在线泡点redis
        CabalAuthVo cabalAuthVo = cabalAuthMapper.getCabalAuthVoByUserNum(account);
        Date loginTime = cabalAuthVo.getLoginTime();
        Integer userNum = cabalAuthVo.getUserNum();
        String key = CABAL_ONLINE_CODE.concat(userNum.toString());
        String redisStatTime = stringRedisTemplate.opsForValue().get(key);
        Date nowTime = new Date();
        long diffMin;
        if(StringUtils.isEmpty(redisStatTime)) {
            diffMin = DateApiUtil.diff(loginTime, nowTime, 60 * 1000);
        } else {
            diffMin = DateApiUtil.diff(DateApiUtil.parse(redisStatTime, DateApiUtil.NORM_DATETIME_PATTERN), nowTime, 60 * 1000);
        }
        log.info("帐号{}-{}退出客户端，本次泡点统计在线时间{}分钟", userNum, cabalAuthVo.getUserId(), diffMin);
        updateAccountCash(userNum, diffMin);
        stringRedisTemplate.delete(key);*/
        return "SUCCESS";
    }

    @Override
    public void updateOnlineUserCash() {
        // 1、查询在线用户，获取在线用户登录时间
        List<CabalAuthVo> cabalAuthVoList = cabalAuthMapper.searchOnlineUserList();
        if(CollectionUtils.isEmpty(cabalAuthVoList)) {
            log.info("暂时没有在线用户");
            return;
        }

        // 2、第一次计算泡点以登录时间为准，用当前系统时间减去登录时间计算泡点，并将记录存入redis
        Date nowTime = new Date();
        Integer cashMultiple = 1;
        DynamicDataSourceContextHolder.setContextKey(SystemConstants.DS_KEY_CABALMALL);
        CabalGatewayGameConfig cabalGatewayGameConfig = cabalGatewayGameConfigMapper.selectById(1);
        DynamicDataSourceContextHolder.removeContextKey();
        if(cabalGatewayGameConfig != null) {
            cashMultiple = cabalGatewayGameConfig.getCashMultiple();
        }

        for(CabalAuthVo cabalAuthVo : cabalAuthVoList) {
            Integer userNum = cabalAuthVo.getUserNum();
            // 根据当前userNum查询Login状态是否在线，如果不在线则不处理
            CabalAuthVo accountVo = cabalAuthMapper.getCabalAuthVoByUserNum(userNum);
            if(accountVo.getLoginState() == 0) {
                continue;
            }

            Integer worldIdx = cabalAuthVo.getWorldIdx();

            // 用于统计玩家在线时间
            String staticOnlineUserNumKey = CABAL_STATIC_ONLINE_CODE.concat(String.valueOf(userNum));
            String staticOnlineTimeStr = stringRedisTemplate.opsForValue().get(staticOnlineUserNumKey);
            Integer staticOnlineTime;
            if(StringUtils.isEmpty(staticOnlineTimeStr)) {
                staticOnlineTime = 10;
            } else {
                staticOnlineTime = Integer.parseInt(stringRedisTemplate.opsForValue().get(staticOnlineUserNumKey));
                staticOnlineTime = staticOnlineTime + 10;
            }
            stringRedisTemplate.opsForValue().set(staticOnlineUserNumKey, String.valueOf(staticOnlineTime));

            Integer vipLevel = accountVo.getVipLevel();
            Date loginTime = cabalAuthVo.getLoginTime();
            String key = CABAL_ONLINE_CODE.concat(userNum.toString());
            String redisStatTime = stringRedisTemplate.opsForValue().get(key);
            long diffMin;
            if(StringUtils.isEmpty(redisStatTime)) {
                diffMin = DateApiUtil.diff(loginTime, nowTime, 60 * 1000);
            } else {
                diffMin = DateApiUtil.diff(DateApiUtil.parse(redisStatTime, DateApiUtil.NORM_DATETIME_PATTERN), nowTime, 60 * 1000);
            }

            if(diffMin > 100) {
                log.info("{}-异常泡点记录>>>>{}", accountVo.getUserId(), diffMin);
                stringRedisTemplate.delete(key);
                stringRedisTemplate.opsForValue().set(key, DateApiUtil.format(nowTime, DateApiUtil.NORM_DATETIME_PATTERN));
                redisStatTime = stringRedisTemplate.opsForValue().get(key);
                log.info("redisStatTime异常泡点redis时间打印>>>>{}", redisStatTime);
                continue;
            }

            if(DateApiUtil.isInTimeRange(LocalTime.of(18, 0), LocalTime.of(23, 0))) {
                diffMin = (long)(diffMin * 2);
            } else {
                diffMin = diffMin * cashMultiple;
            }

            // 血色冰峰挂店处理
            int iUserNum = cabalAuthVo.getUserNum();
            int startCharacterId = iUserNum * 8;
            int endCharacterId = startCharacterId + 5;
            CabalCharacter cabalCharacter = cabalAuthMapper.getCabalCharacterByCharacterId(startCharacterId, endCharacterId);
            if(cabalCharacter == null) {
                continue;
            }

            DynamicDataSourceContextHolder.setContextKey(SystemConstants.DS_KEY_CABALMALL);
            if(cabalCharacter.getWorldIdx() == 1) {
                QueryWrapper<CabalCharacterInfo> queryWrapper = new QueryWrapper<CabalCharacterInfo>();
                queryWrapper.lambda().eq(CabalCharacterInfo::getCharacterId, cabalCharacter.getCharacterIdx());
                CabalCharacterInfo cabalCharacterInfo = cabalCharacterInfoMapper.selectOne(queryWrapper);
                if(cabalCharacterInfo != null && cabalCharacterInfo.getShopState() == 1) {
                    diffMin = (long)(diffMin * 1.2);
                }
            }

            if(vipLevel != null && vipLevel > 1) {
                diffMin = (long)(diffMin * (1 + Double.parseDouble(onlineCashVipBasicMultiplier) * (vipLevel - 1)));
            }

            updateAccountCash(userNum, diffMin, accountVo.getUserId());
            DynamicDataSourceContextHolder.removeContextKey();
            stringRedisTemplate.opsForValue().set(key, DateApiUtil.format(nowTime, DateApiUtil.NORM_DATETIME_PATTERN));
            log.info("帐号{}-{}-{}本次泡点统计在线时间{}分钟,所在地图>>>>{}",
                    userNum,
                    getCharsetName(cabalAuthVo.getCharacterName()),
                    stringRedisTemplate.opsForValue().get(accountVo.getUserId()),
                    diffMin,
                    worldIdx);
        }

        // 清理已下线帐号redis数据
        Set<String> allKeys = stringRedisTemplate.keys(CABAL_ONLINE_CODE + "*");
        log.info("-----------key size------------" + allKeys.size());
        for(String key : allKeys) {
            log.info("==============redis 存在的在线泡点key:" + key);
            // 查询帐号是否在线，如果离线则删除
            String[] arrayKey = key.split(CABAL_ONLINE_CODE);
            String userNum = arrayKey[1];
            CabalAuthVo cabalAuthVo = cabalAuthMapper.getCabalAuthVoByUserNum(Integer.parseInt(userNum));
            if(cabalAuthVo == null) {
                stringRedisTemplate.delete(key);
                continue;
            }

            if(cabalAuthVo.getLoginState() == 1) {
                continue;
            }
            stringRedisTemplate.delete(key);
        }
    }

    private String getCharsetName(String dbName) {
        Charset latin1Charset = Charset.forName("ISO-8859-1");
        Charset gbkCharset = Charset.forName("GBK");

        byte[] latin1Bytes = dbName.getBytes(latin1Charset);
        String decodedText = new String(latin1Bytes, gbkCharset);
        return decodedText;
    }

    @Transactional
    protected void updateAccountCash(Integer userNum, Long diffMin, String account) {
        // 查询数据库是否存在对应userNum的记录
        QueryWrapper<CabalMallCoinAccount> queryWrapper = new QueryWrapper<CabalMallCoinAccount>();
        queryWrapper.lambda().eq(CabalMallCoinAccount::getUserNum, userNum);
        CabalMallCoinAccount cabalMallCoinAccount =  cabalMallCoinAccountMapper.selectOne(queryWrapper);
        if(cabalMallCoinAccount == null) {
            CabalMallCoinAccount newCabalMallCoinAccount = new CabalMallCoinAccount();
            newCabalMallCoinAccount.setId(SnowflakeIdUtil.getInstance().nextId());
            newCabalMallCoinAccount.setMallCoinTotal(diffMin.intValue());
            newCabalMallCoinAccount.setUserNum(userNum);
            newCabalMallCoinAccount.setAccount(account);
            newCabalMallCoinAccount.setUpdateTime(new Timestamp(new Date().getTime()));
            cabalMallCoinAccountMapper.insert(newCabalMallCoinAccount);
            insertCabalMallCoinLog(userNum, 0, diffMin.intValue(), newCabalMallCoinAccount.getId(),diffMin);
            return;
        }

        Integer mallCoinPre = cabalMallCoinAccount.getMallCoinTotal();
        cabalMallCoinAccount.setUpdateTime(new Timestamp(new Date().getTime()));
        cabalMallCoinAccount.setAccount(account);
        cabalMallCoinAccount.setMallCoinTotal(cabalMallCoinAccount.getMallCoinTotal() + diffMin.intValue());
        cabalMallCoinAccountMapper.updateById(cabalMallCoinAccount);

        // 插入商城币变更日志
        insertCabalMallCoinLog(userNum, mallCoinPre, cabalMallCoinAccount.getMallCoinTotal(), cabalMallCoinAccount.getId(), diffMin);
    }

    private void insertCabalMallCoinLog(Integer userNum, Integer mallCoinPre, Integer mallCoinAfter, Long eventId, Long diffMin) {
        CabalMallCoinLog cabalMallCoinLog = new CabalMallCoinLog();
        cabalMallCoinLog.setId(SnowflakeIdUtil.getInstance().nextId());
        cabalMallCoinLog.setUserNum(userNum);
        cabalMallCoinLog.setMallCoinPre(mallCoinPre);
        cabalMallCoinLog.setMallCoinAfter(mallCoinAfter);
        cabalMallCoinLog.setLogType(2);
        cabalMallCoinLog.setLogDateTime(new Timestamp(new Date().getTime()));
        cabalMallCoinLog.setEventId(eventId);
        cabalMallCoinLog.setChangeMallCoin(diffMin.intValue());
        cabalMallCoinLogMapper.insert(cabalMallCoinLog);
    }


    private String getMd5ValueByFileName(String fileName) {
        File file = CabalGatewayFileUtil.getLocalFile("/cabal/" + fileName);
        return CabalGatewayMD5Util.getFileMd5(file);
    }

    @Override
    public List<CabalRankInfoDTO> searchRankInfoList(CabalAuthDTO cabalAuthDTO) {
        List<CabalRankInfoDTO> cabalRankInfoDTOList = cabalAuthMapper.searchRankInfoList();
        for(CabalRankInfoDTO cabalRankInfoDTO : cabalRankInfoDTOList) {
            cabalRankInfoDTO.setCharacterName(getCharsetName(cabalRankInfoDTO.getCharacterName()));
        }
        return cabalRankInfoDTOList;
    }

    @Override
    public CabalMallAuthDTO getAccountInfo(String userNum, String authKey, HttpServletRequest request) {
        DynamicDataSourceContextHolder.removeContextKey();
        CabalMallAuthDTO cabalMallAuthDTO = cabalAuthMapper.getAccountInfo(userNum, authKey);
        if(cabalMallAuthDTO == null) {
            return new CabalMallAuthDTO();
        }
        try {
            cabalMallAuthDTO.setCreateTime(DateApiUtil.format(cabalMallAuthDTO.getDCreateTime(), DateApiUtil.NORM_DATETIME_PATTERN));
            cabalMallAuthDTO.setLastLoginTime(DateApiUtil.format(cabalMallAuthDTO.getDLastLoginTime(), DateApiUtil.NORM_DATETIME_PATTERN));

            cabalMallAuthDTO.setOnlineTime(new BigDecimal(cabalMallAuthDTO.getOnlineTime()).divide(new BigDecimal("60"), 2, RoundingMode.HALF_UP).toString());

            // 查询数据库是否存在对应userNum的记录
            QueryWrapper<CabalMallCoinAccount> queryWrapper = new QueryWrapper<CabalMallCoinAccount>();
            queryWrapper.lambda().eq(CabalMallCoinAccount::getUserNum, userNum);
            DynamicDataSourceContextHolder.setContextKey(SystemConstants.DS_KEY_CABALMALL);
            CabalMallCoinAccount cabalMallCoinAccount =  cabalMallCoinAccountMapper.selectOne(queryWrapper);
            if(cabalMallCoinAccount == null) {
                cabalMallAuthDTO.setMallCoinTotal("0");
            } else {
                cabalMallAuthDTO.setMallCoinTotal(String.valueOf(cabalMallCoinAccount.getMallCoinTotal()));
            }

            // 查询贡献积分
            QueryWrapper<CabalContributePointAccount> contributePointQueryWrapper = new QueryWrapper<CabalContributePointAccount>();
            contributePointQueryWrapper.lambda().eq(CabalContributePointAccount::getUserNum, Integer.parseInt(userNum));
            CabalContributePointAccount dbCabalContributePointAccount =  cabalContributePointAccountMapper.selectOne(contributePointQueryWrapper);
            cabalMallAuthDTO.setContributePoint(dbCabalContributePointAccount == null ? 0:dbCabalContributePointAccount.getContributePoint());

            // 麒麟BUFF到期时间
            QueryWrapper<CabalCharacterGmBuff> queryGmBuffWrapper = new QueryWrapper<CabalCharacterGmBuff>();
            queryGmBuffWrapper.lambda().eq(CabalCharacterGmBuff::getAccount, cabalMallAuthDTO.getUserId());
            DynamicDataSourceContextHolder.setContextKey(SystemConstants.DS_KEY_CABALMALL);
            CabalCharacterGmBuff dbCabalCharacterGmBuff = cabalCharacterGmBuffMapper.selectOne(queryGmBuffWrapper);
            if(dbCabalCharacterGmBuff != null) {
                cabalMallAuthDTO.setDExpirationTime(DateApiUtil.format(dbCabalCharacterGmBuff.getExpirationDate(), DateApiUtil.NORM_DATETIME_PATTERN));
            }

            // 查询在线角色信息
            int iUserNum = Integer.parseInt(userNum);
            int startCharacterId = iUserNum * 8;
            int endCharacterId = startCharacterId + 5;

            CabalCharacter cabalCharacter = cabalAuthMapper.getCabalCharacterByCharacterId(startCharacterId, endCharacterId);
            if(cabalCharacter != null) {
                cabalMallAuthDTO.setOnlineCharacterIdx(cabalCharacter.getCharacterIdx());
                cabalMallAuthDTO.setNation(cabalCharacter.getNation());
            }

            if(cabalMallAuthDTO.getVipLevel() == null) {
                cabalMallAuthDTO.setVipLevel(1);
            }

            // 累计在线时长
            String staticOnlineUserNumKey = CABAL_STATIC_ONLINE_CODE.concat(userNum);
            String staticOnlineStaticStr = stringRedisTemplate.opsForValue().get(staticOnlineUserNumKey);
            if(StringUtils.isBlank(staticOnlineStaticStr)) {
                staticOnlineStaticStr = "0";
            }
            Integer staticOnlineTime = Integer.parseInt(staticOnlineStaticStr);
            cabalMallAuthDTO.setAccumulateOnlineMinutes(staticOnlineTime);

        } finally {
            DynamicDataSourceContextHolder.removeContextKey();
        }
        return cabalMallAuthDTO;
    }

    @Override
    public List<CabalCharacterInfoDTO> searchCharacterList(String account) {
        DynamicDataSourceContextHolder.removeContextKey();
        CabalAuthVo cabalAuthVo = cabalAuthMapper.getCabalAuthVoByAccount(account);
        Integer userNum = cabalAuthVo.getUserNum();
        List<CabalCharacterInfoDTO> cabalCharacterInfoDTOList = new ArrayList<>();
        int startCharacterId = userNum * 8;
        int endCharacterId = startCharacterId + 5;

        List<CabalCharacter> cabalCharacterList = cabalAuthMapper.searchCabalCharacterListByUserNum(startCharacterId, endCharacterId);
        // 查询数据库是否存在对应userNum的记录
        QueryWrapper<CabalMallCoinAccount> queryWrapper = new QueryWrapper<CabalMallCoinAccount>();
        queryWrapper.lambda().eq(CabalMallCoinAccount::getUserNum, userNum);
        DynamicDataSourceContextHolder.setContextKey(SystemConstants.DS_KEY_CABALMALL);
        CabalMallCoinAccount cabalMallCoinAccount =  cabalMallCoinAccountMapper.selectOne(queryWrapper);

        Long totalAlz = cabalAuthMapper.getAccountTotalAlz(userNum);
        if(totalAlz == null) totalAlz = 0l;

        for(CabalCharacter cabalCharacter : cabalCharacterList) {
            CabalCharacterInfoDTO cabalCharacterInfoDTO = new CabalCharacterInfoDTO();
            cabalCharacterInfoDTO.setCharacterIdx(cabalCharacter.getCharacterIdx());
            cabalCharacterInfoDTO.setMapIdx(cabalCharacter.getWorldIdx());
            cabalCharacterInfoDTO.setUserNum(userNum);
            cabalCharacterInfoDTO.setZy(cabalCharacter.getProfession());
            cabalCharacterInfoDTO.setLoginState(cabalCharacter.getLoginState());
            cabalCharacterInfoDTO.setCharacterName(getCharsetName(cabalCharacter.getName()));
            cabalCharacterInfoDTO.setLastLoginTime(DateApiUtil.format(cabalAuthVo.getLoginTime(), DateApiUtil.NORM_DATETIME_PATTERN));
            if(cabalAuthVo.getLogoutTime() != null) {
                cabalCharacterInfoDTO.setLastLoginOutTime(DateApiUtil.format(cabalAuthVo.getLogoutTime(), DateApiUtil.NORM_DATETIME_PATTERN));
            } else {
                cabalCharacterInfoDTO.setLastLoginOutTime("");
            }

            if(cabalMallCoinAccount != null && cabalMallCoinAccount.getMallCoinTotal() != null) {
                cabalCharacterInfoDTO.setCoinTotal(cabalMallCoinAccount.getMallCoinTotal());
            } else {
                cabalCharacterInfoDTO.setCoinTotal(0);
            }

            // GMBUFF
            QueryWrapper<CabalCharacterGmBuff> queryGmBuffWrapper = new QueryWrapper<CabalCharacterGmBuff>();
            queryGmBuffWrapper.lambda().eq(CabalCharacterGmBuff::getAccount, account);
            DynamicDataSourceContextHolder.setContextKey(SystemConstants.DS_KEY_CABALMALL);
            CabalCharacterGmBuff dbCabalCharacterGmBuff = cabalCharacterGmBuffMapper.selectOne(queryGmBuffWrapper);
            DynamicDataSourceContextHolder.removeContextKey();
            if(dbCabalCharacterGmBuff != null) {
                cabalCharacterInfoDTO.setBuffExpirationDate(DateApiUtil.format(dbCabalCharacterGmBuff.getExpirationDate(), DateApiUtil.NORM_DATETIME_PATTERN));
            }

            cabalCharacterInfoDTO.setAuthType(cabalAuthVo.getAuthType());
            cabalCharacterInfoDTO.setTotalAlz(totalAlz);

            // 查询VIP等级
            CabalAuth cabalAuth = cabalAuthMapper.selectById(userNum);
            cabalCharacterInfoDTO.setVipLevel(cabalAuth.getVipLevel());
            cabalCharacterInfoDTOList.add(cabalCharacterInfoDTO);
        }
        return cabalCharacterInfoDTOList;
    }

    @Override
    public String setTitleByCharacterIdx(String characterIdx, Integer titleId) {
        CabalTitleData cabalTitleData = cabalAuthMapper.getTitleDataByCharacterId(Integer.parseInt(characterIdx));
        String hexResult = HighLowSwapUtil.byteToHex(cabalTitleData.getTitleData());
        String addHex = HighLowSwapUtil.lowHigh(titleId).concat("00000000");
        String updateHex = hexResult.concat(addHex);
        byte[] updateBytes = HighLowSwapUtil.hexStringToByteArray(updateHex); // 将BigInteger对象转换成byte数组
        cabalAuthMapper.updateTitleData(Integer.parseInt(characterIdx), updateBytes);
        return "SUCCESS";
    }

    @Override
    public String unsealAccount(String account) {
        DynamicDataSourceContextHolder.removeContextKey();
        log.info("全面解封账号>>>>{}", account);
        CabalAuthVo cabalAuthVo = cabalAuthMapper.getCabalAuthVoByAccount(account);
        String lastLoginIp = cabalAuthVo.getLastLoginIp();
        log.info("全面解封账号lastLoginIp>>>>{}", lastLoginIp);
        String ip = cabalAuthVo.getIp();
        log.info("全面解封账号ip>>>>{}", ip);
        String phone = cabalAuthVo.getPhone();
        log.info("全面解封账号phone>>>>{}", phone);

        // 解封手机号对应账号
        cabalAuthMapper.unsealAccountByPhone(phone);

        // 解封IP对应账号
        cabalAuthMapper.unsealAccountByIp(ip, lastLoginIp);

        // 从redis删除黑名单
        stringRedisTemplate.delete(SystemConstants.CABAL_BLACK_PHONE_KEY.concat(phone));

        List<CabalAuthVo> cabalAuthVoList = cabalAuthMapper.searchCabalAuthListByIp(ip);
        if(!CollectionUtils.isEmpty(cabalAuthVoList) && cabalAuthVoList.size() > 1) {
            for(CabalAuthVo cabalAuth : cabalAuthVoList) {
                stringRedisTemplate.delete(SystemConstants.CABAL_BLACK_PHONE_KEY.concat(cabalAuth.getPhone()));
            }
        }
        return "SUCCESS";
    }

    @Value("${externalCheats:SpeedGame;Speed;齿轮;变速}")
    private String externalCheats;

    @Override
    public String banAccount(String account, String externalCheatsProcessName) {
        log.info("检测到账号{}开外挂，执行封禁操作， 外挂程序名称>>>>{}", account, externalCheatsProcessName);
        String[] externalCheatsArray = externalCheats.split(";");
        boolean isMatch = false;
        for(String externalCheat : externalCheatsArray) {
            if(externalCheat.equals(externalCheatsProcessName)) {
                isMatch = true;
                break;
            }
        }

        if(isMatch) {
            cabalAuthMapper.banAccount(account);
            return "SUCCESS";
        }

        log.info("外挂检测封禁操作非外挂程序不做处理>>>>{}", externalCheatsProcessName);
        return "FAIL";
    }

    @Override
    public String updateNation(CabalUpdateNationDTO cabalUpdateNationDTO) {
        String oldNation = cabalUpdateNationDTO.getNation();
        Integer nation;
        if("1".equals(oldNation) || "0".equals(oldNation)) {
            nation = 2;
        } else {
            nation = 1;
        }
        DynamicDataSourceContextHolder.setContextKey(SystemConstants.DS_KEY_CABALMALL);
        Integer characterId = Integer.parseInt(cabalUpdateNationDTO.getOnlineCharacterIdx());
        QueryWrapper<CabalCharacterNation> queryWrapper = new QueryWrapper<CabalCharacterNation>();
        queryWrapper.lambda().eq(CabalCharacterNation::getCharacterId, characterId);
        CabalCharacterNation dbCabalCharacterNation =  cabalCharacterNationMapper.selectOne(queryWrapper);
        if(dbCabalCharacterNation == null) {
            CabalCharacterNation cabalCharacterNation = new CabalCharacterNation();
            cabalCharacterNation.setCharacterId(characterId);
            cabalCharacterNation.setNation(nation);
            cabalCharacterNation.setId(SnowflakeIdUtil.getInstance().nextId());
            cabalCharacterNationMapper.insert(cabalCharacterNation);
        } else {
            CabalCharacterNation updateCabalCharacterNation = new CabalCharacterNation();
            updateCabalCharacterNation.setId(dbCabalCharacterNation.getId());
            updateCabalCharacterNation.setNation(nation);
            cabalCharacterNationMapper.updateById(updateCabalCharacterNation);
        }
        cabalAuthMapper.updateNationByCharacterId(Integer.parseInt(cabalUpdateNationDTO.getOnlineCharacterIdx()), nation);
        return "SUCCESS";
    }

    @Override
    public void onlineCharacterCheck() {
        List<CabalCharacter> cabalCharacterList = cabalAuthMapper.selectOnlineCharacterList();
        log.info("当前全服在线人数>>>>{}", cabalCharacterList.size());
        for(CabalCharacter cabalCharacter : cabalCharacterList) {
            Integer worldIdx = cabalCharacter.getWorldIdx();
            Integer userNum = cabalCharacter.getCharacterIdx() / 8;
            Integer channelIdx = cabalCharacter.getChannelIdx();
            String charName = getCharsetName(cabalCharacter.getName());
            CabalAuthVo accountVo = cabalAuthMapper.getCabalAuthVoByUserNum(userNum);
            if(accountVo.getLoginState() == 0) {
                QueryWrapper<CabalAuth> updateWrapper = new QueryWrapper<CabalAuth>();
                updateWrapper.lambda().eq(CabalAuth::getUserNum, userNum);
                CabalAuth updateCabalAuth = new CabalAuth();
                updateCabalAuth.setLoginState(1);
                cabalAuthMapper.update(updateCabalAuth, updateWrapper);
                log.info("修正在线账号>>>>{}的登录状态", accountVo.getUserId());
            }
            log.info("当前主地图在线人员userNum>>>>{}-账号>>>>{}-角色名称>>>>{}-频道>>>>{}-地图>>>>{}", userNum, accountVo.getUserId(), charName, channelIdx, worldIdx);
        }
    }

    @Override
    public String updateGatewayConfig(UpdateGatewayConfigDTO updateGatewayConfigDTO) {
        CabalGatewayGameConfig updateCabalGatewayGameConfig = new CabalGatewayGameConfig();
        BeanUtils.copyProperties(updateGatewayConfigDTO, updateCabalGatewayGameConfig);
        updateCabalGatewayGameConfig.setId(1L);
        DynamicDataSourceContextHolder.setContextKey(SystemConstants.DS_KEY_CABALMALL);
        cabalGatewayGameConfigMapper.updateById(updateCabalGatewayGameConfig);
        DynamicDataSourceContextHolder.removeContextKey();

        // 重新加载缓存校验文件数据
        setFileMd5ResultMap();

        return "SUCCESS";
    }

    @Override
    public UpdateGatewayConfigDTO getUpdateGatewayConfigDTO() {
        UpdateGatewayConfigDTO updateGatewayConfigDTO = new UpdateGatewayConfigDTO();
        DynamicDataSourceContextHolder.setContextKey(SystemConstants.DS_KEY_CABALMALL);
        CabalGatewayGameConfig cabalGatewayGameConfig = cabalGatewayGameConfigMapper.selectById(1);
        BeanUtils.copyProperties(cabalGatewayGameConfig, updateGatewayConfigDTO);
        DynamicDataSourceContextHolder.removeContextKey();
        return updateGatewayConfigDTO;
    }

    @Override
    public List<CabalCashBackPackDTO> searchCashBackPackList(String account) {
        DynamicDataSourceContextHolder.removeContextKey();
        CabalAuthVo cabalAuthVo = cabalAuthMapper.getCabalAuthVoByAccount(account);
        List<CabalCashBackPackDTO> cabalCashBackPackDTOList = cabalAuthMapper.searchCashBackPackList(cabalAuthVo.getUserNum());
        for(CabalCashBackPackDTO cabalCashBackPackDTO : cabalCashBackPackDTOList) {
            cabalCashBackPackDTO.setCashAccount(account);
            if(StringUtils.isEmpty(cabalCashBackPackDTO.getRegDate())) {
                cabalCashBackPackDTO.setRegDate("");
            } else {
                cabalCashBackPackDTO.setRegDate(DateApiUtil.format(DateApiUtil.parseStrToDate(cabalCashBackPackDTO.getRegDate(), "yyyy-MM-dd HH:mm:ss.SSS"), DateApiUtil.NORM_DATETIME_PATTERN));
            }

            if(StringUtils.isEmpty(cabalCashBackPackDTO.getUseDate())) {
                cabalCashBackPackDTO.setUseDate("");
            } else {
                cabalCashBackPackDTO.setUseDate(DateApiUtil.format(DateApiUtil.parseStrToDate(cabalCashBackPackDTO.getUseDate(), "yyyy-MM-dd HH:mm:ss.SSS"), DateApiUtil.NORM_DATETIME_PATTERN));
            }
        }
        return cabalCashBackPackDTOList;
    }

    @Override
    public String updateCashBackPackDurationIdx(String account) {
        DynamicDataSourceContextHolder.removeContextKey();
        CabalAuthVo cabalAuthVo = cabalAuthMapper.getCabalAuthVoByAccount(account);
        cabalAuthMapper.updateCashBackPackDurationIdx(cabalAuthVo.getUserNum());
        return "SUCCESS";
    }

    @Override
    public String delCharacter(Integer characterIdx) {
        cabalAuthMapper.delCharacter(characterIdx);
        return "SUCCESS";
    }

    @Override
    public String clearCashRecord(String account) {
        DynamicDataSourceContextHolder.removeContextKey();
        CabalAuthVo cabalAuthVo = cabalAuthMapper.getCabalAuthVoByAccount(account);
        cabalAuthMapper.clearCashRecord(cabalAuthVo.getUserNum());
        return null;
    }

    @Override
    public String updateVipLevel(String account, Integer vipLevel) {
        QueryWrapper<CabalAuth> updateWrapper = new QueryWrapper<CabalAuth>();
        updateWrapper.lambda().eq(CabalAuth::getId, account);
        CabalAuth updateCabalAuth = new CabalAuth();
        updateCabalAuth.setVipLevel(vipLevel);
        cabalAuthMapper.update(updateCabalAuth, updateWrapper);
        return "SUCCESS";
    }

    @Override
    public void cabalAuthControl() {
        DynamicDataSourceContextHolder.removeContextKey();
        // 1、异常登录账号直接封停操作a't
        cabalAuthMapper.batchUpdateAbnormalLoginAccounts();

        // 2、下线账号状态重置为0
        cabalAuthMapper.batchResetAccountsLoginState();

    }

    @Override
    public String getVipSpecialEffectsStatus(String characterIdValue) {
        DynamicDataSourceContextHolder.removeContextKey();
        log.info("角色ID>>>>" + characterIdValue);
        CabalAuthVo  cabalAuthVo = cabalAuthMapper.getCabalAuthVoByUserNum(Integer.parseInt(characterIdValue) / 8);
        if(cabalAuthVo.getVipSpecialEffectsStatus() == null) {
            return "0";
        }
        return cabalAuthVo.getVipSpecialEffectsStatus().toString();
    }

    @Override
    public String updateVipSpecialEffectsStatus(String characterIdValue) {
        DynamicDataSourceContextHolder.removeContextKey();
        String vipSpecialEffectsStatus = this.getVipSpecialEffectsStatus(characterIdValue);
        QueryWrapper<CabalAuth> updateWrapper = new QueryWrapper<CabalAuth>();
        updateWrapper.lambda().eq(CabalAuth::getUserNum, Integer.parseInt(characterIdValue) / 8);
        CabalAuth updateCabalAuth = new CabalAuth();
        if("1".equals(vipSpecialEffectsStatus)) {
            updateCabalAuth.setVipSpecialEffectsStatus(0);
        } else {
            updateCabalAuth.setVipSpecialEffectsStatus(1);
        }
        cabalAuthMapper.update(updateCabalAuth, updateWrapper);
        return "SUCCESS";
    }


}
